package com.apache.passport.jwt;

import com.apache.jwt.Claims;
import com.apache.jwt.JwtBuilder;
import com.apache.jwt.Jwts;
import com.apache.jwt.SignatureAlgorithm;
import com.apache.passport.entity.UctUser;
import com.apache.tools.StrUtil;
import com.apache.uct.common.ToolsUtil;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {

    @Value("${spring.profiles.active}")
    private String profiles;

    private static JwtUtil jwtUtil;

    private Logger log = Logger.getLogger(getClass());

    /**
     * 生成加密key
     * 1、openssl genrsa -out private_key.pem 2048
     * 2、openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
     * 3、openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
     */

    private JwtUtil() {
    }

    public static JwtUtil newInstance() {
        if (null == jwtUtil) {
            jwtUtil = new JwtUtil();
        }
        return jwtUtil;
    }

    /**
     * description:  统一创建jwt
     */
    public String uniCreateJwt(String id, String subject, String aud, long ttlMillis,
            String userInfo) {
        String exeType = StrUtil
                .doNull(ToolsUtil.getInstance().getValueByKey("secret_key_type"), "rsa");
        if ("rsa".equalsIgnoreCase(exeType)) {
            return createJwtRsa(id, subject, aud, ttlMillis, userInfo);
        } else {
            return createJWT(id, subject, aud, ttlMillis, userInfo);
        }
    }

    /**
     * 统一解密jwt
     */
    public Claims uniParseJWT(String jwt) {
        Claims claims = null;
        try {
            String exeType = StrUtil
                    .doNull(ToolsUtil.getInstance().getValueByKey("secret_key_type"), "");
            if ("rsa".equalsIgnoreCase(exeType)) {
                claims = parseJwtRsa(jwt);
            } else {
                claims = parseJWT(jwt);
            }
        } catch (Exception e) {
            log.warn(e);
        }
        return claims;
    }
    ////////////以下AES方式////////////////////////

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    private SecretKey generalKey() {
        String stringKey = profiles + Constant.JWT_SECRET;
        byte[] encodedKey = Base64.decodeBase64(stringKey.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 创建jwt
     *
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public String createJWT(String id, String subject, String aud, long ttlMillis,
            String userInfo) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;//.RS256;//
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        try {
            SecretKey key = generalKey();
            Map<String, Object> map = new HashMap<String, Object>(1);
            map.put("u_info", userInfo);
            JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject)
                    .setAudience(aud).setIssuer(ToolsUtil.getInstance().getValueByKey("login.url"))
                    .addClaims(map).signWith(signatureAlgorithm, key);
            if (ttlMillis >= 0) {
                long expMillis = nowMillis + ttlMillis;
                Date exp = new Date(expMillis);
                builder.setExpiration(exp);
            }
            return builder.compact();
        } catch (Exception e) {
            log.warn(e);
        }
        return "";
    }

    /**
     * 解密jwt
     *
     * @param jwt
     * @return
     */
    public Claims parseJWT(String jwt) {
        Claims claims = null;
        SecretKey key = generalKey();
        claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();
        return claims;
    }

    ////////////////以下是RSA方式////////////////////////////////////////////////////

    /**
     * 生成加密key
     */
    private KeyPair getKeyPair(int type) throws Exception {
        KeyPair keyPair = null;
        if (0 == type) {//私密KEY，用来加密
            String filename = ToolsUtil.getInstance().getValueByKey("rsa_private_key");
            byte[] keyBytes = FileUtils.readFileToByteArray(new File(filename));
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            keyPair = new KeyPair(null, kf.generatePrivate(spec));
        } else {//公密KEY，用来解密
            String filename = ToolsUtil.getInstance().getValueByKey("ras_public_key");
            byte[] keyBytes = FileUtils.readFileToByteArray(new File(filename));
            X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            keyPair = new KeyPair(kf.generatePublic(spec), null);
        }
        return keyPair;
    }

    /**
     * 创建jwt
     */
    public String createJwtRsa(String id, String subject, String aud, long ttlMillis,
            String userInfo) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;//
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        try {
            KeyPair key = getKeyPair(0);
            Map<String, Object> map = new HashMap<String, Object>(1);
            map.put("u_info", userInfo);
            JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject)
                    .setAudience(aud).setIssuer(ToolsUtil.getInstance().getValueByKey("login.url")).
                            addClaims(map).signWith(signatureAlgorithm, key.getPrivate());
            if (ttlMillis >= 0) {
                long expMillis = nowMillis + ttlMillis;
                Date exp = new Date(expMillis);
                builder.setExpiration(exp);
            }
            return builder.compact();
        } catch (Exception e) {
            log.warn(e);
        }
        return "";
    }

    /**
     * 解密jwt(RSA)
     *
     * @param jwt
     * @return
     */
    public Claims parseJwtRsa(String jwt) throws Exception {
        Claims claims = null;
        KeyPair key = getKeyPair(1);
        claims = Jwts.parser().setSigningKey(key.getPublic()).parseClaimsJws(jwt).getBody();
        return claims;
    }

    /**
     * 生成subject信息
     *
     * @param user
     * @return
     */
    public String generalSubject(UctUser user) {
        JSONObject jo = new JSONObject();
        jo.put("userId", user.getUserId());
        jo.put("userEname", user.getUserEname());
        jo.put("email", StrUtil.doNull(user.getEmail(), ""));
        jo.put("mobile", StrUtil.doNull(user.getMobile(), ""));
        jo.put("orgEname", StrUtil.doNull(user.getOrgEname(), ""));
        jo.put("userType", StrUtil.doNull(user.getUserType(), ""));
        return jo.toString();
    }

}